package org.rakvag.hylla.spotifyapi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;

@Service
public class SpotifyAPIImpl implements SpotifyAPI {

	private static final Logger logger = Logger.getLogger(SpotifyAPIImpl.class.getName());
	private static final int MAX_ANTALL_TRAADER = 30;
	private static final int TIMEOUT_TRAADER = 60;

	@Override
	public List<SpotifyAlbum> soekEtterAlbum(String artist, String album, int maxForsek) {
		artist = StringUtils.trimToNull(artist);
		album = StringUtils.trimToNull(album);

		if (artist == null && album == null)
			return new ArrayList<SpotifyAlbum>();

		artist = haandterSpesialtegn(artist);
		album = haandterSpesialtegn(album);
		StringBuilder soekestreng = new StringBuilder(artist == null ? "" : "artist:" + artist);
		if (artist != null && album != null)
			soekestreng.append("+");
		soekestreng.append(album == null ? "" : "album:" + album);

		boolean proevEnGangTil = false;
		int antallForsoek = 0;
		StringBuffer soekerespons = null;
		do {
			try {
				antallForsoek++;
				soekerespons = utfoerSoekEtterAlbum(soekestreng);
				proevEnGangTil = false;
			} catch (BadGatewayException bge) {
				if (antallForsoek >= maxForsek)
					throw bge;
				proevEnGangTil = true;
			}

		} while (proevEnGangTil);

		SearchResult soekeResultat = SearchResult.parseJsonSearchResult(soekerespons.toString());

		if (soekeResultat.getAlbums() != null && soekeResultat.getAlbums().size() > 0) {
			return soekeResultat.getAlbums();
		} else
			return new ArrayList<SpotifyAlbum>();
	}

	@Override
	public Collection<SpotifyAlbum> hentAlbumPaaSpotifyURIer(Collection<String> spotifyURIer, int maksForsoek) {
		List<SearchResult> resultatene = hentPaaSpotifyURIer(new ArrayList<String>(spotifyURIer), maksForsoek);
		Collection<SpotifyAlbum> albumene = new HashSet<SpotifyAlbum>();
		for (SearchResult resultat : resultatene)
			albumene.add(resultat.getAlbum());
		return albumene;
	}

	@Override
	public Map<String, SpotifyArtist> hentArtisterPaaSpotifyURIer(Collection<String> spotifyURIer, int maksForsoek) {
		List<SearchResult> resultatene = hentPaaSpotifyURIer(new ArrayList<String>(spotifyURIer), maksForsoek);
		Map<String, SpotifyArtist> artistene = new HashMap<String, SpotifyArtist>();
		for (SearchResult resultat : resultatene) {
			artistene.put(resultat.getArtist().getHref(), resultat.getArtist());
		}

		return artistene;
	}

	@Override
	public SpotifyArtist hentArtistPaaSpotifyURI(String spotifyURI, int maksForsoek) {
		SearchResult searchResult = SpotifyLookup.kjoerLookup(spotifyURI, maksForsoek);
		return searchResult != null ? searchResult.getArtist() : null;
	}

	@Override
	public Map<String, SpotifyTrack> hentTracksPaaSpotifyURIer(Collection<String> spotifyURIer, int maksForsoek) {
		List<SearchResult> resultatene = hentPaaSpotifyURIer(new ArrayList<String>(spotifyURIer), maksForsoek);
		Map<String, SpotifyTrack> tracks = new HashMap<String, SpotifyTrack>();
		for (SearchResult resultat : resultatene) {
			SpotifyTrack track = resultat.getTrack();
			tracks.put(track.getHref(), track);
		}
		return tracks;
	}

	@Override
	public Map<String, String> hentBildelinker(Collection<String> spotifyURIer) {
		List<BildelinkLookup> traader = new ArrayList<BildelinkLookup>();
		for (String spotifyURI : spotifyURIer)
			traader.add(new BildelinkLookup(spotifyURI));

		List<Future<BildelinkInfo>> traadTasks = null;
		ExecutorService executor = Executors.newFixedThreadPool(MAX_ANTALL_TRAADER);
		try {
			traadTasks = executor.invokeAll(traader, TIMEOUT_TRAADER, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			throw new RuntimeException(e.getMessage(), e);
		}

		Set<BildelinkInfo> resultatene = new HashSet<BildelinkInfo>();
		for (Future<BildelinkInfo> task : traadTasks) {
			try {
				resultatene.add(task.get());
			} catch (InterruptedException e) {
				throw new RuntimeException(e.getMessage(), e);
			} catch (ExecutionException e) {
				throw new RuntimeException(e.getMessage(), e);
			}
		}

		return BildelinkInfo.lagMap(resultatene);
	}

	@Override
	public String hentBildelink(String artistURI) {
		BildelinkInfo info = BildelinkLookup.hentBildeLink(artistURI);
		return info != null ? info.bildelink : null;
	}

	private List<SearchResult> hentPaaSpotifyURIer(List<String> spotifyURIer, int maksForsoek) {
		List<SpotifyLookup> traader = new ArrayList<SpotifyLookup>();
		for (String spotifyURI : spotifyURIer) {
			if (spotifyURI != null)
				traader.add(new SpotifyLookup(spotifyURI, 10));
		}

		List<Future<SearchResult>> traadTasks = null;
		ExecutorService executor = Executors.newFixedThreadPool(MAX_ANTALL_TRAADER);
		try {
			traadTasks = executor.invokeAll(traader, TIMEOUT_TRAADER, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			throw new RuntimeException(e.getMessage(), e);
		}

		List<SearchResult> resultatene = new ArrayList<SearchResult>();
		for (Future<SearchResult> task : traadTasks) {
			try {
				resultatene.add(task.get());
			} catch (InterruptedException e) {
				throw new RuntimeException(e.getMessage(), e);
			} catch (ExecutionException e) {
				throw new RuntimeException(e.getMessage(), e);
			}
		}

		return resultatene;
	}

	private StringBuffer utfoerSoekEtterAlbum(StringBuilder soekestreng) {
		StringBuffer soekerespons = new StringBuffer();
		logger.info("Kaller Spotify");
		try {
			URL url = new URL("http://ws.spotify.com/search/1/album.json?q=" + soekestreng.toString());
			BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
			String line = null;
			while ((line = reader.readLine()) != null) {
				soekerespons.append(line);
			}
			reader.close();
		} catch (MalformedURLException e) {
			logger.error("Feil ved kall mot Spotify");
			logger.error(e.getMessage());
			throw new RuntimeException(e.getMessage(), e);
		} catch (IOException e) {
			if (e.getMessage().contains("Server returned HTTP response code: 502"))
				throw new BadGatewayException(e.getMessage(), e);

			logger.error("Feil ved kall mot Spotify");
			logger.error(e.getMessage());
			throw new RuntimeException(e.getMessage(), e);
		}
		return soekerespons;
	}

	private String haandterSpesialtegn(String streng) {
		if (streng == null)
			return null;

		streng = streng.replace(" ", "+");
		streng = streng.replace("æ", "ae");
		streng = streng.replace("ø", "o");
		streng = streng.replace("å", "a");
		streng = streng.replace("ä", "a");
		streng = streng.replace("ö", "o");
		streng = streng.replace("Æ", "AE");
		streng = streng.replace("Ø", "O");
		streng = streng.replace("Å", "A");
		streng = streng.replace("Ä", "A");
		streng = streng.replace("Ö", "O");
		return streng;
	}

}
